#include "pluginhandler.h"
#include "../gui/tabscreen.h"
#include "../gui/whaletabbase.h"

#include <QApplication>
#include <QPluginLoader>
#include <QtDebug>
#include <QDir>
#include <QSettings>

PluginHandler::PluginHandler( QDir pluginsDir ) {
    this->mPluginsDir = pluginsDir;
}

QMap< QString, WhalePluginStarter * > PluginHandler::starters() {
    return this->mStarters;
}

void PluginHandler::loadPlugins() {
    WhalePlugin * plugin;
    WhalePluginStarter * starter = NULL;
    QString name;

    // for each file in the plugins directory
    foreach ( QString fileName, this->mPluginsDir.entryList( QDir::Files ) ) {
        // get plugin as QObject
        plugin = this->getPluginObject( fileName );

        if ( !plugin ) { // test if load succeeded
            continue;
        }

        switch ( plugin->Type ) {
            case TabPlugin:
                starter = plugin->getTabInterface()->generateStarter();
                if ( starter ) {
                    starter->setPlugin( plugin );
                    this->mStarters.insert( plugin->Name, starter );

                    connect( starter, SIGNAL( triggered() ), this, SLOT( requestTab() ) );
                    connect( plugin, SIGNAL( stateChanged( bool ) ), starter, SLOT( setVisible( bool ) ) );
                }
                break;
            case Unknown:
                return; // don't insert to plugins map, just leave function
        }

        this->mPlugins.insert( plugin->Name, plugin );
    }
}

QMap< QString, WhalePlugin * > PluginHandler::plugins() {
    return this->mPlugins;
}

WhalePlugin * PluginHandler::getPluginObject( QString fileName ) {
    QObject * pluginObj = NULL;
    QPluginLoader loader( this->mPluginsDir.absoluteFilePath( fileName ) );
    
    if ( !loader.load() ) {
        qDebug() << "could not load: " << fileName;
        return NULL;
    }

    if ( !( pluginObj = loader.instance() ) ) {
        qDebug() << "could not construct";
        return NULL;
    }

    return new WhalePlugin( pluginObj );
}

void PluginHandler::requestTab() {
    // find the starter that sent the signal
    WhalePluginStarter * starter = qobject_cast< WhalePluginStarter * >( sender() );
    
    // get the associated plugin
    WhalePlugin * plugin = starter->plugin();

    // convert to tabinterface
    TabInterface * tabPlugin = plugin->getTabInterface();
    WhaleTabBase * tab = tabPlugin->generateTab(); // create a new default tab
    tab->setPlugin( tabPlugin ); // associate tab with plugin

    connect( plugin, SIGNAL( disabled() ), tab, SLOT( close() ) );

    emit newTabRequest( tab ); // emit signal to be catched by tab manager
}

WhaleTabBase * PluginHandler::recreateTab( QString pluginName, QVariant constructors ) {
    WhalePlugin * plugin = this->getPlugin( pluginName );

    if ( plugin == NULL ) {
        // plugin was removed or disabled
        return NULL;
    }

    // recreate the tab using the same constructors
    WhaleTabBase * tab = plugin->getTabInterface()->restoreTab( constructors );

    // associate tab with plugin
    tab->setPlugin( plugin->getTabInterface() );

    // close tab when plugin gets disabled
    connect( plugin, SIGNAL( disabled() ), tab, SLOT( close() ) ); 

    return tab;
}

WhalePlugin * PluginHandler::getPlugin( QString name ) {
    QMap< QString, WhalePlugin * >::iterator it = this->mPlugins.find( name );

    if ( it == this->mPlugins.end() || it.key() != name ) {
        // plugin removed
        return NULL;
    }

    // check if plugin is enabled
    if ( !it.value()->Enabled ) {
        return NULL;
    }

    return it.value();
}

void PluginHandler::enablePlugin( QString name ) {
    QMap< QString, WhalePlugin * >::iterator it = this->mPlugins.find( name );

    if ( it == this->mPlugins.end() ) {
        // no such plugin
        return;
    }

    // get plugin from iterator
    WhalePlugin * plugin = it.value();
    plugin->setEnabled( true );
}

void PluginHandler::disablePlugin( QString name ) {
    QMap< QString, WhalePlugin * >::iterator it = this->mPlugins.find( name );

    if ( it == this->mPlugins.end() ) {
        // no such plugin
        return;
    }

    WhalePlugin * plugin = it.value();
    plugin->setEnabled( false );
}

void PluginHandler::restorePluginStates() {
    QSettings settings;
    int size = settings.beginReadArray( "plugins" );
    for ( int i = 0; i < size; ++i ) {
        settings.setArrayIndex( i );
        QString pluginName = settings.value( "name" ).toString();
        bool enabled = settings.value( "state" ).toBool();

        if ( enabled ) {
            this->enablePlugin( pluginName );
        }
        else {
            this->disablePlugin( pluginName );
        }
    }
}

void PluginHandler::savePluginStates() { // for tabstate look at TabManager
    QSettings settings( "SocialWhale", "SocialWhale" );
    settings.beginWriteArray( "plugins" );
    int index = 0;
    foreach ( WhalePlugin * plugin, this->mPlugins ) {
        settings.setArrayIndex( index++ );
        settings.setValue( "name", plugin->Name );
        settings.setValue( "state", plugin->Enabled );
    }
    settings.endArray();
}
